﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net.Mime;
using System.Text;
using System.Web;
using System.Web.Script.Serialization;
using System.Web.UI.WebControls;
using DVK.SP2007.Moss;
using DVK.SP2007.Constants;
using DVK.SP2007.Controls;
using DVK.SP2007.Resources;
using DVK.SP2007.UniversalClient;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.WebControls;
using System.Web.UI.HtmlControls;
using DVK.SP2007.Views;
using DVK.SP2007.Presenters;

namespace DVK.SP2007.Pages
{
    /// <summary>
    /// Allows user to send document to DVK. Contains all the logic to gather the required data and create the outbox listitem.
    /// </summary>
    public partial class SendToDVK : LayoutsPageBase, ISendToDvkView
    {
        //TODO: Reorganize code
        #region global variables

        Dictionary<string, string> _errors;
        public Dictionary<string, string> Errors
        {
            get
            {
                if (_errors == null)
                    _errors = new Dictionary<string, string>();
                return _errors;
            }
        }

        public PeopleEditor Author 
        {
            get { return this.peAuthor; }
        }

        public PeopleEditor Compilators
        {
            get { return this.peCompilators; }
        }

        public string DocumentName { get { return this.txtDocumentName.Text; } }

        public string Comments {get { return this.txtComments.Text; }}
        public string DocumentLanguage { get { return this.txtDocumentLanguage.Text; }}
        public string DocumentType { get { return this.txtDocumentType.Text; }}
        public string DocumentVersion { get { return this.txtDocumentVersion.Text; }}
        public string DvkFolder { get { return this.txtDvkFolder.Text; }}
        public string DvkType { get { return this.txtDvkType.Text; }}
        public string MessageTitle { get { return this.txtTitle.Text; }}
        public bool UseAuthorAsCompany { get { return cbUseAuthorCompanyInfo.Checked; } }

        protected const string SessionRecipients = "SessionRecipients";
        protected SPWeb _web;
        protected string _listId;
        protected string _itemUrl;
        private SPListItem _item;
        private SPList _list;

        protected CheckBox cbUseAuthorCompanyInfo;

        /// <summary>
        /// txtSendDate input field
        /// </summary>
        public DateTime? SendDate
        {
            get
            {
                var currentValue = this.txtSendDate.Value;

                DateTime temp;
                if (DateTime.TryParse(currentValue, out temp))
                {
                    return temp;
                }
                return null;
            }
            set
            {
                if (value.HasValue)
                    this.txtSendDate.Value = value.Value.ToShortDateString();
            }
        }
        #endregion

        /// <summary>
        /// Gets the current list.
        /// </summary>
        /// <value>The current list.</value>
        protected SPList CurrentList
        {
            get
            {
                if (_list == null)
                    _list = _web.Lists[new Guid(_listId)];

                return _list;
            }
        }
        /// <summary>
        /// Gets the current item.
        /// </summary>
        /// <value>The current item.</value>
        protected SPListItem CurrentItem
        {
            get
            {
                if (_item == null)
                    _item = _web.GetListItem(_itemUrl);
                return _item;
            }
        }

        #region Events

        /// <summary>
        /// Handles the Load event of the Page control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected void Page_Load(object sender, EventArgs e)
        {
            _listId = Request.Params[Params.QueryStringParameterListId];
            _itemUrl = Request.Params[Params.QueryStringParameterItemUrl];
            _web = SPContext.Current.Web;

            //If item is folder, then it cant be sent to DVK
            if (CurrentItem.FileSystemObjectType == SPFileSystemObjectType.Folder)
            {
                ShowError(DvkWeb.SendToDVK_ErrorCannotSendFolder);
                //Hide inputform
                inputFormContent.Visible = false;
                //Hide Send button
                btnSend.Visible = false;
                return;
            }

            //If its not postpack, add default data
            if (!this.IsPostBack)
            {
                //Default fields
                FillDefaultFields();
                FillAuthorAndCompilatorFields(CurrentItem.File);

                //set MappedMetadata viewer provider 
                var mappedMetadataProvider = new OutMappedMetadataProvider(CurrentItem);
                (this.mappedMetadata as MappedMetadataViewer).MappedMetadataProvider = mappedMetadataProvider;
                
                //Bind organisation dropdownlist
                var organisations = new OrganisationRepository(_web).GetDvkCapableOrganisations();

                this.ddlOrganisations.DataTextField = "Name";
                this.ddlOrganisations.DataValueField = "Code";
                this.ddlOrganisations.SelectedIndex = 0;
                this.ddlOrganisations.DataSource = organisations;
                this.ddlOrganisations.DataBind();

                if (organisations.Count > 0)
                    FillPersonsList(organisations.First(), null);

                //Set empty recipients list
                var recipients = new List<SenderReceiverInfo>();
                this.listRecipients.DataSource = recipients;
                this.listRecipients.DataBind();
                SetRecipientsToSession(recipients);
            }

            peAuthor.Validate();
            peCompilators.Validate();
        }

        /// <summary>
        /// Handles the SelectedIndexChanged event of the ddlOrganisations control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected void ddlOrganisations_SelectedIndexChanged(object sender, EventArgs e)
        {
            FillPersonsList(
                    new UnitInfo()
                    {
                        Name = this.ddlOrganisations.SelectedItem.Text,
                        Code = this.ddlOrganisations.SelectedItem.Value
                    },
                    null
                );
        }

        /// <summary>
        /// Handles the Click event of the btnAddNewOrganisationPerson control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected void btnAddNewOrganisationPerson_Click(object sender, EventArgs e)
        {
            OrganisationRecipientsRepository repository = new OrganisationRecipientsRepository(_web);
            repository.AddNewOrganisationRecipient(
                                    txtPersonalCode.Text, ddlOrganisations.SelectedItem.Value, txtFirstName.Text,
                                    txtSurName.Text, txtEmail.Text, txtJobTitle.Text, txtDepartment.Text, txtWorkPhone.Text
                                    );

            //Updating persons dropdownlist
            FillPersonsList(
                    new UnitInfo()
                    {
                        Name = this.ddlOrganisations.SelectedItem.Text,
                        Code = this.ddlOrganisations.SelectedItem.Value
                    },
                    txtPersonalCode.Text
                );
            //Clean the organisation recipient information form
            CleanPersonForm();
        }

        /// <summary>
        /// Handles the Click event of the btnAddRecipient control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected void btnAddRecipient_Click(object sender, EventArgs e)
        {
            //if no organisation has been selected
            if (ddlOrganisations.SelectedIndex == -1)
            {
                ShowError(DvkWeb.SendToDVK_OrganisationMustBeSelected);
                return;
            }

            var recipients = GetRecipientsFromSession();

            var personCode = string.Empty;
            //If recipient person has been selected take the person code
            if (ddlOrganisationPerson.SelectedIndex != -1)
            {
                personCode = ddlOrganisationPerson.SelectedItem.Value;
            }

            //If there already is a recipient from the same organisation, then it cant be added again.
            foreach (var recipient in recipients)
            {
                //if the organisatsion code is correct and person info is missing and personcode is "" or 
                if ((recipient.Organisation.Code == ddlOrganisations.SelectedItem.Value) &&
                    ((recipient.Person == null && personCode == string.Empty) || (recipient.Person != null && recipient.Person.Code == personCode)))
                {
                    ShowError(DvkWeb.SendToDVK_ErrorCannotAddSameRecipientTwice);
                    return;
                }
            }

            //get recipient person
            OrganisationRecipientsRepository organisationRecipientManager = new OrganisationRecipientsRepository(_web);
            var personItem = organisationRecipientManager.FindRecipient(ddlOrganisations.SelectedItem.Value, personCode);

            //At least organisation info is required for recipient
            var newRecipient = new SenderReceiverInfo()
            {
                Organisation = new UnitInfo()
                {
                    Code = ddlOrganisations.SelectedItem.Value,
                    Name = ddlOrganisations.SelectedItem.Text
                }
            };

            //If person exists, we can take additional metadata from it
            if (personItem != null)
            {
                newRecipient.Person =
                        new UnitInfo()
                        {
                            Code = ddlOrganisationPerson.SelectedItem.Value,
                            Name = ddlOrganisationPerson.SelectedItem.Text
                        };
                newRecipient.Email = personItem[Fields.Email.Name] as string;
                newRecipient.JobTitle = personItem[Fields.JobTitle.Name] as string;
                newRecipient.Telephone = personItem[Fields.WorkPhone.Name] as string;
                newRecipient.OrganisationUnit =
                        new UnitInfo()
                        {
                            Name = personItem[Fields.Department.Name] as string
                        };
            }

            recipients.Add(newRecipient);
            SetRecipientsToSession(recipients);

            //Bind data to listview control
            this.listRecipients.DataSource = recipients;
            this.listRecipients.DataBind();
        }

        /// <summary>
        /// Handles the Command event of the btnDelete control.
        /// <remarks>
        /// Deletes the recipient from organisation recipients listview.
        /// </remarks>
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Web.UI.WebControls.CommandEventArgs"/> instance containing the event data.</param>
        protected void btnDelete_Command(object sender, CommandEventArgs e)
        {
            //Can be called on exact row and such the recipient must exist
            string[] codes = (e.CommandArgument as string).Split(';');
            string organisationCode = codes[0];
            string personCode = codes[1];

            var recipients = GetRecipientsFromSession();

            SenderReceiverInfo itemToDelete = null;
            foreach (var item in recipients)
            {
                if (item.Organisation.Code.Equals(organisationCode) &&
                    ((item.Person == null && personCode == string.Empty) || (item.Person != null && item.Person.Code.Equals(personCode))))
                {
                    itemToDelete = item;
                    break;
                }
            }

            if (itemToDelete == null)
                return;

            recipients.Remove(itemToDelete);

            this.listRecipients.DataSource = recipients;
            this.listRecipients.DataBind();
            SetRecipientsToSession(recipients);
        }

        /// <summary>
        /// Handles the Click event of the btnSaada control.
        /// <remarks>
        /// Contains all the logic to send the document to DVK (create listitem to the outbox list).
        /// </remarks>
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected void btnSaada_Click(object sender, EventArgs e)
        {
            SendToDvkPresenter presenter = new SendToDvkPresenter(this, _web);
            presenter.Send();

            #region Commented out - For Removal
            /* For removal after testing
             * Refactored to Presenter.
             * 
            //Message recipients
            var recipients = GetRecipientsFromSession();

            //If there are no recipients, there is nobody to send the message
            if (recipients.Count == 0)
            {
                Errors.Add("Recipients", DvkWeb.SendToDVK_ErrorAtLeastOneRecipientIsRequired);
                //ShowError(DvkWeb.SendToDVK_ErrorAtLeastOneRecipientIsRequired);
                //return;
            }

            //Validate input
            if (peAuthor.ResolvedEntities.Count == 0)
            {
                Errors.Add("Author", DvkWeb.SendToDVK_ErrorMustEnterAuthor);
                //ShowError(DvkWeb.SendToDVK_ErrorMustEnterAuthor);
                //return;
            }

            if (peCompilators.ResolvedEntities.Count == 0)
            {
                Errors.Add("Compilators", DvkWeb.SendToDVK_ErrorMustEnterCompilators);
                //ShowError(DvkWeb.SendToDVK_ErrorMustEnterCompilators);
                //return;
            }

            if (Errors.Count > 0)
            {
                ShowErrors();
                return;
            }
            //end validate input

            SPListItem newItem = null;
            bool redirect = true; //If some operation fails, redirect should not happen and error mus tbe shown
            //Get outbox list
            SPList outboxList = _web.GetListFromUrl(Urls.ListOutbox);

            try
            {
                //NOTE: Not in used anymore, only numerator in use now.
                //Before adding while, we check if it already exists. If yes and it has been sent already, 
                //we will move the file automatically to history, to avoid overwriting and keeping the history
                //Throws exception if same file is in sending status!
                //MessageArchiver.CheckOutboxAndMoveToHistory(_web, targetUrl);

                var targetUrl = InboxOutboxItemHelper.GetValidTargetUrl(_web, outboxList, txtTitle.Text);

                SPUser messageSender = _web.CurrentUser;
                PersonMetadata messageSenderMetadata = null;

                //New item contetTypeId
                var contentTypeId = outboxList.ContentTypes.BestMatch(
                        new SPContentTypeId(ContentTypes.OutboxDocument.ID)
                    );

                //Getting nessecary Dvk settings in json form
                DvkSettingsManager settingsManager = new DvkSettingsManager(_web);
                string jsonPersonFieldsMapping = settingsManager.GetSetting(Constants.Settings.DvkPersonMetadataFieldsMapping);
                string jsonOrganisationInfo = settingsManager.GetSetting(Constants.Settings.DvkSenderOrganisationInfo);

                //json serializer
                JavaScriptSerializer jsonSerilizer = new JavaScriptSerializer();

                //Getting author and compilators information
                PersonMetadata author = null;
                bool useAuthorOrganisation = false;
                if (this.cbUseAuthorCompanyInfo.Checked)
                {
                    useAuthorOrganisation = true;
                }
                List<PersonMetadata> compilators = new List<PersonMetadata>();

                //Getting information from User Information List. To assure we can access metadata, we run with elevated priviledges.
                SPSecurity.RunWithElevatedPrivileges(delegate()
                {
                    IUserInfoManager<string> mossUserInfoManager =
                        new MossUserInfoManager(new UserInfoFieldMapping(jsonPersonFieldsMapping));

                    //sender information
                    messageSenderMetadata = mossUserInfoManager.GetUserInfo(messageSender.LoginName);

                    //Getting author info
                    if (peAuthor.Accounts.Count > 0)
                    {
                        SPUser spAuthor = _web.EnsureUser(peAuthor.Accounts[0].ToString());
                        author = mossUserInfoManager.GetUserInfo(spAuthor.LoginName);
                    }

                    //getting compilers info
                    foreach (string item in peCompilators.Accounts)
                    {
                        SPUser compilatorUser = _web.EnsureUser(item);
                        compilators.Add(mossUserInfoManager.GetUserInfo(compilatorUser.LoginName));
                    }

                    #region WSSUserInfoManager - sample
                    //Use WSSUserInfoManager to make it work with WSS.
                    //IUserInfoManager<int> wssUserInfoManager = 
                    //    new WssUserInfoManager(
                    //            _web, 
                    //            Constants.ListUserInformationUrl, 
                    //            new UserInfoFieldMapping(jsonPersonFieldsMapping)
                    //        );

                    //// implementation details omitted
                    //messageSenderMetadata = wssUserInfoManager.GetUserInfo(messageSender.ID);

                    ////Getting author info
                    //if (peAuthor.Accounts.Count > 0)
                    //{
                    //    SPUser spAuthor = _web.EnsureUser(peAuthor.Accounts[0].ToString());
                    //    author = wssUserInfoManager.GetUserInfo(spAuthor.ID);
                    //}

                    ////getting compilers info
                    //foreach (string item in peCompilators.Accounts)
                    //{
                    //    SPUser compilatorUser = _web.EnsureUser(item);
                    //    compilators.Add(wssUserInfoManager.GetUserInfo(compilatorUser.ID));
                    //}
                    #endregion
                });

                //Getting file data
                SPFile currentFile = CurrentItem.File;
                byte[] data = currentFile.OpenBinary();

                //if the document is not .ddoc, then we make it.
                //if (!currentFile.Name.EndsWith(General.DDOCExtension, StringComparison.OrdinalIgnoreCase))
                //{
                    DigiDocFileData newDigiDocFileData = new DigiDocFileData();
                    newDigiDocFileData.Contents = data;
                    newDigiDocFileData.ContentType = DigiDocFileData.ContentTypeEmbeddedBase64;
                    newDigiDocFileData.Size = data.Length;
                    newDigiDocFileData.Filename = txtDocumentName.Text;
                    newDigiDocFileData.MimeType = DigiDocFileData.MimeTypeFrom(data, MediaTypeNames.Application.Octet);

                    SignedDoc signedDoc = new SignedDoc(newDigiDocFileData);
                    data = (new UTF8Encoding()).GetBytes(signedDoc.ToString());
                //}

                //Now we start creating the new item. First we setup creation properties.
                var properties = new Hashtable
                                         {
                                             {FieldNames.SPFileContentTypeIdProperty, contentTypeId.ToString()}
                                         };

                //Add new file with creation properties
                SPFile newFile = outboxList.RootFolder.Files.Add(targetUrl, data, properties, true);

                //Get SPListItem from file
                newItem = newFile.Item;

                IDvkMessage message =
                    new OutgoingMessageMapper(_web, CurrentItem, newItem)
                    {
                        UseAuthorOrganisation = useAuthorOrganisation,
                        Author = author,
                        Comments = txtComments.Text,
                        Compilators = compilators,
                        DocumentLanguage = txtDocumentLanguage.Text,
                        DocumentType = txtDocumentType.Text,
                        DocumentVersion = txtDocumentVersion.Text,
                        DvkFolder = txtDvkFolder.Text,
                        DvkType = txtDvkType.Text,
                        Organisation = jsonSerilizer.Deserialize<UnitInfo>(jsonOrganisationInfo),
                        Recipients = recipients,
                        SendDate = (SendDate.HasValue) ? SendDate.Value : DateTime.Now,
                        Sender = messageSenderMetadata,
                        Title = txtTitle.Text
                    }.Map();

                newItem = new OutgoingMessageToListitemMapper(CurrentItem, message, newItem, new AspNetResourceManager()).Map();

                newItem.UpdateOverwriteVersion();
            }
            catch (Exception exc)
            {
                //If exception occurs, we cancel sending, show error message and delete the item, that might have been added to outbox list already
                if (newItem != null)
                {
                    newItem.Delete();
                }

                ShowError(DvkWeb.SendToDVK_ErrorSendFailed, exc.ToString());
                redirect = false;
            }
            //This should not be in try block, weird errors can occur.
            if (redirect)
            {
                RedirectToSource();
            }
            */
#endregion
        }

        public void RedirectToSource()
        {
            string url = Request.Params[Params.QueryStringParameterSource];
            if (!string.IsNullOrEmpty(url))
            {
                SPUtility.Redirect(
                    url,
                    SPRedirectFlags.Default,
                    HttpContext.Current);
            }
        }

        #endregion

        /// <summary>
        /// Fills the default fields on the form.
        /// </summary>
        private void FillDefaultFields()
        {
            this.txtTitle.Text = CurrentItem.Title;
            this.txtDvkType.Text = CurrentItem.ContentType.Name;
            this.SendDate = DateTime.Now;
            this.txtDvkFolder.Text = "/";

            //Document info
            SPFile file = CurrentItem.File;
            txtCreationDate.Text = ((DateTime)CurrentItem[Constants.FieldNames.ItemFieldCreatedDate]).ToShortDateString();
            txtDocumentName.Text = CurrentItem.Name;
            txtDocumentType.Text = CurrentItem.ContentType.Name;
            txtDocumentVersion.Text = file.UIVersionLabel;
        }

        private void FillAuthorAndCompilatorFields(SPFile file)
        {
            //Default author is the document author
            ArrayList author = new ArrayList();
            author.Add(
                new PickerEntity()
                {
                    Key = file.Author.LoginName
                }
                );
            peAuthor.UpdateEntities(author);

            //Default compiler is the document author
            ArrayList compilators = new ArrayList();
            compilators.Add(
                new PickerEntity()
                {
                    Key = file.Author.LoginName
                }
                );
            peCompilators.UpdateEntities(compilators);
        }

        /// <summary>
        /// Fills the persons list based on organisation.
        /// <remarks>
        /// personCode is used to select the person from the dropdownlist .
        /// </remarks>
        /// </summary>
        /// <param name="organisation">The organisation.</param>
        /// <param name="personCode">The person code.</param>
        private void FillPersonsList(UnitInfo organisation, string personCode)
        {
            var items =
                new OrganisationRecipientsRepository(_web)
                    .GetOrganisationRecipients(ddlOrganisations.SelectedItem.Value);

            //Creating new list
            this.ddlOrganisationPerson.Items.Clear();

            this.ddlOrganisationPerson.Items.Add(
                        new ListItem()
                        {
                            Text = string.Empty,
                            Value = string.Empty
                        }
                    );

            foreach (SPListItem item in items)
            {
                string name = item[Fields.FirstName.Name] + " " + item[Fields.SurName.Name];
                string itemPersonCode = item[Fields.RecipientPersonCode.Name] as string;
                bool selected = (!string.IsNullOrEmpty(personCode) && personCode == itemPersonCode) ? true : false;
                this.ddlOrganisationPerson.Items.Add(
                        new ListItem()
                        {
                            Text = name,
                            Value = itemPersonCode,
                            Selected = selected
                        }
                    );
            }
        }

        /// <summary>
        /// Cleans the new organisation recipient information form.
        /// </summary>
        protected void CleanPersonForm()
        {
            txtPersonalCode.Text = string.Empty;
            txtFirstName.Text = string.Empty;
            txtSurName.Text = string.Empty;
            txtEmail.Text = string.Empty;
            txtJobTitle.Text = string.Empty;
            txtDepartment.Text = string.Empty;
            txtWorkPhone.Text = string.Empty;
        }

        /// <summary>
        /// Gets recipients list from session.
        /// </summary>
        /// <returns></returns>
        private List<SenderReceiverInfo> GetRecipientsFromSession()
        {
            var recipients = Session[SessionRecipients] as List<SenderReceiverInfo>;
            if (recipients == null)
                recipients = new List<SenderReceiverInfo>();
            return recipients;
        }
        /// <summary>
        /// Sets recipients list to session.
        /// </summary>
        /// <param name="recipients">The recipients.</param>
        private void SetRecipientsToSession(List<SenderReceiverInfo> recipients)
        {
            Session[SessionRecipients] = recipients;
        }

        public void ShowErrors()
        {
            lbErrorMessage.Text += "<ul>";
            foreach (var error in Errors)
            {
                lbErrorMessage.Text += "<li>" + error.Value + "</li>";
            }
            lbErrorMessage.Text += "</ul>";
        }

        #region ISendToDvkView Members


        public string ItemUrl
        {
            get { return _itemUrl; }
        }

        public string ListId
        {
            get { return _listId; }
        }

        /// <summary>
        /// Shows the error in lbErrorMessage.
        /// </summary>
        /// <param name="text">The text.</param>
        /// <param name="data">The data.</param>
        public void ShowError(string text, params object[] data)
        {
            //TODO: Such show error should be in a base page. And a common control should be used for all pages to show error. (Taavi, Date)
            this.lbErrorMessage.Text = string.Format(text, data);
        }

        #endregion
    }
}